bitkeeper revision 1.698.1.1 (4022a73eOGiwh6ZU9-EU4LZoLcVnuQ)
authorach61@labyrinth.cl.cam.ac.uk <ach61@labyrinth.cl.cam.ac.uk>
Thu, 5 Feb 2004 20:27:42 +0000 (20:27 +0000)
committerach61@labyrinth.cl.cam.ac.uk <ach61@labyrinth.cl.cam.ac.uk>
Thu, 5 Feb 2004 20:27:42 +0000 (20:27 +0000)
pervasive debugger

16 files changed:
.rootkeys
README.CD
docs/pdb.txt [new file with mode: 0644]
tools/nsplitd/Makefile [new file with mode: 0644]
tools/nsplitd/nsplitd.c [new file with mode: 0644]
xen/arch/i386/pdb-stub.c [new file with mode: 0644]
xen/arch/i386/setup.c
xen/arch/i386/traps.c
xen/common/debug-linux.c [new file with mode: 0644]
xen/common/debug-linux.h [new file with mode: 0644]
xen/common/debug.c
xen/common/kernel.c
xen/drivers/char/xen_serial.c
xen/include/asm-i386/pdb.h [new file with mode: 0644]
xen/include/hypervisor-ifs/dom0_ops.h
xen/include/xeno/sched.h

index 65a76aac8bfd747ace8147ee8d89592b9d0cb4ed..703a59d1e7b2054a5a9c7c70d583a13183647f20 100644 (file)
--- a/.rootkeys
+++ b/.rootkeys
@@ -12,6 +12,7 @@
 3f9e7d60PWZJeVh5xdnk0nLUdxlqEA docs/eps/xenlogo.eps
 3f9e7d63lTwQbp2fnx7yY93epWS-eQ docs/figs/dummy
 3f9e7d564bWFB-Czjv1qdmE6o0GqNg docs/interface.tex
+4022a73cgxX1ryj1HgS-IwwB6NUi2A docs/pdb.txt
 3f9e7d58t7N6hjjBMxSn-NMxBphchA docs/style.tex
 3f9e7d5bz8BwYkNuwyiPVu7JJG441A docs/xenstyle.cls
 3f815144d1vI2777JI-dO4wk49Iw7g extras/mini-os/Makefile
@@ -73,6 +74,8 @@
 3f13d81e6Z6806ihYYUw8GVKNkYnuw tools/misc/xen_nat_enable.README
 3f1668d4F29Jsw0aC0bJEIkOBiagiQ tools/misc/xen_read_console.c
 3f87ba90EUVPQLVOlFG0sW89BCwouQ tools/misc/xen_refresh_dev.c
+4022a73cEKvrYe_DVZW2JlAxobg9wg tools/nsplitd/Makefile
+4022a73cKms4Oq030x2JBzUB426lAQ tools/nsplitd/nsplitd.c
 3fbca441SjQr8vJwTQIgH1laysaWog tools/xc/Makefile
 3fbba6dbDfYvJSsw9500b4SZyUhxjQ tools/xc/lib/Makefile
 3fbba6dc1uU7U3IFeF6A-XEOYF2MkQ tools/xc/lib/rpm.spec
 3ddb79bdIKgipvGoqExEQ7jawfVowA xen/arch/i386/pci-i386.h
 3ddb79bdHe6_Uij4-glW91vInNtBYQ xen/arch/i386/pci-irq.c
 3ddb79bcZ_2FxINljqNSkqa17ISyJw xen/arch/i386/pci-pc.c
+4022a73czgX7d-2zfF_cb33oVemApQ xen/arch/i386/pdb-stub.c
 3ddb79bc1_2bAt67x9MFCP4AZrQnvQ xen/arch/i386/process.c
 3ddb79bc7KxGCEJsgBnkDX7XjD_ZEQ xen/arch/i386/rwlock.c
 3ddb79bcrD6Z_rUvSDgrvjyb4846Eg xen/arch/i386/setup.c
 3ddb79bddEYJbcURvqqcx99Yl2iAhQ xen/common/block.c
 3ddb79bdrqnW93GR9gZk1OJe1qK-iQ xen/common/brlock.c
 3fb10d07GscSWPKxBqpvNfU-dYfa0g xen/common/console.c
+4022a73c_BbDFd2YJ_NQYVvKX5Oz7w xen/common/debug-linux.c
+4022a73c_KPZ1VEbYOrpAhQffd01kA xen/common/debug-linux.h
 3fa152581E5KhrAtqZef2Sr5NKTz4w xen/common/debug.c
 3ddb79bdLX_P6iB7ILiblRLWvebapg xen/common/dom0_ops.c
 3e6377e4i0c9GtKN65e99OtRbw3AZw xen/common/dom_mem_ops.c
 3ddb79c3xjYnrv5t3VqYlR4tNEOl4Q xen/include/asm-i386/page.h
 3e450943kzme29HPCtq5HNOVQkddfw xen/include/asm-i386/param.h
 3ddb79c3ysKUbxZuwKBRK3WXU2TlEg xen/include/asm-i386/pci.h
+4022a73diKn2Ax4-R4gzk59lm1YdDg xen/include/asm-i386/pdb.h
 3ddb79c3nm2zdzeO6Mj8g7ex3txgGw xen/include/asm-i386/pgalloc.h
 3ddb79c2QF5-pZGzuX4QukPCDAl59A xen/include/asm-i386/processor.h
 3ddb79c3mbqEM7QQr3zVq7NiBNhouA xen/include/asm-i386/ptrace.h
index a69b165ea318439301af417260bc246468861d10..f4478b853a23ba681a4e17769e7747aeeb164aa6 100644 (file)
--- a/README.CD
+++ b/README.CD
@@ -240,10 +240,16 @@ that may be able to help diagnose problems:
 
  ifname=dummy    Don't use any network interface.
 
- ser_baud=xxx    Enable serial I/O and set the baud rate.
+ ser_baud=xxx    Enable serial I/O and set the baud rate (COM1)
 
  dom0_mem=xxx    Set the initial amount of memory for domain0.
-                 
+
+ pdb=xxx          Enable the pervasive debugger.  See docs/pdb.txt
+                  xxx defines how the gdb stub will communicate:
+                     com1    use com1
+                     com1H   use com1 (with high bit set)
+                     com2    use on com2
+                     com2H   use com2 (with high bit set)
 
 It's probably a good idea to join the Xen developer's mailing list on
 Sourceforge:    http://lists.sourceforge.net/lists/listinfo/xen-devel   
diff --git a/docs/pdb.txt b/docs/pdb.txt
new file mode 100644 (file)
index 0000000..aab57c5
--- /dev/null
@@ -0,0 +1,215 @@
+Pervasive Debugging 
+===================
+
+040205 Alex Ho (alex.ho@cl.cam.ac.uk)
+
+Introduction
+------------
+
+The pervasive debugging project is leveraging Xen to 
+debug distributed systems.  We have added a gdb stub
+to Xen to allow for remote debugging of both Xen and
+guest operating systems.  More information about the
+pervasive debugger is available at: http://www.cl.cam.ac.uk/netos/pdb
+
+
+Implementation
+--------------
+
+The gdb stub communicates with gdb running over a serial line.
+The main entry point is pdb_handle_exception() which is invoked
+from:    pdb_key_pressed()    ('D' on the console)
+         do_int3_exception()  (interrupt 3: breakpoint exception)
+         do_debug()           (interrupt 1: debug exception)
+
+This accepts characters from the serial port and passes gdb
+commands to pdb_process_command() which implements the gdb stub
+interface.  This file draws heavily from the kgdb project and
+sample gdbstub provided with gdb.
+
+The stub can examine registers, single step and continue, and
+read and write memory (in Xen, a domain, or a Linux process'
+address space).  The debugger does not currently trace the 
+current process, so all bets are off if context switch occurs
+in the domain.
+
+
+Setup
+-----
+
+ +-------+ telnet +-----------+ serial +-------+ 
+ |  GDB  |--------|  nsplitd  |--------|  Xen  |
+ +-------+        +-----------+        +-------+ 
+
+To run pdb, Xen must be appropriately configured and 
+a suitable serial interface attached to the target machine.
+GDB and nsplitd can run on the same machine.
+
+Xen Configuration
+
+  Add the "pdb=xxx" option to your Xen boot command line
+  where xxx is one of the following values:
+     com1    gdb stub should communicate on com1
+     com1H   gdb stub should communicate on com1 (with high bit set)
+     com2    gdb stub should communicate on com2
+     com2H   gdb stub should communicate on com2 (with high bit set)
+
+  Symbolic debugging infomration is quite helpful too:
+  xeno.bk/xen/arch/i386/Rules.mk
+    add -g to CFLAGS to compile Xen with symbols
+  xeno.bk/xenolinux-2.4.24-sparse/arch/xeno/Makefile
+    add -g to CFLAGS to compile XenoLinux with symbols
+
+  You may also want to consider dedicating a register to the
+  frame pointer (disable the -fomit-frame-pointer compile flag).
+
+  When booting Xen and domain 0, look for the console text 
+  "Initializing pervasive debugger (PDB)" just before DOM0 starts up.
+
+Serial Port Configuration
+
+  pdb expects to communicate with gdb using the serial port.  Since 
+  this port is often shared with the machine's console output, pdb can
+  discriminate its communication by setting the high bit of each char.
+
+  A new tool has been added to the source tree which splits 
+  the serial output from a remote machine into two streams: 
+  one stream (without the high bit) is the console and 
+  one stream (with the high bit stripped) is the pdb communication.
+
+  See:  xeno.bk/tools/nsplitd
+
+  Note: nsplitd was originally written for the Nemesis project
+  at Cambridge.
+
+  Usage:
+    %telnet <hostname> <port>
+    This is the console of the remote machine.  You will probably
+    want to set telnet in char mode (or create a .telnetrc file).
+    <hostname> and <port> are for your instance of nsplitd.
+    You should be able to press 'h' to display a list of keyboard
+    handlers.  In particular, you should see:
+       key 'D' (ascii '44') => enter pervasive debugger
+
+  After nsplitd accepts a connection on <port>, it starts listening
+  on port <port + 1>.  Characters sent to the <port + 1> will have the
+  high bit set and vice versa for characters received.
+
+  Note: if you are not using a serial console and can dedicate your
+  serial line to pdb messages, then edit pdb_put_char and pdb_get_char 
+  in pdb-stub.c to remove the high bit tests.
+
+GDB 6.0
+  pdb has been tested with gdb 6.0.  It should also work with
+  earlier versions.
+
+
+Usage
+-----
+
+1. Boot Xen and XenoLinux
+2. Interrupt Xen by pressing 'D' at the console
+   You should see the console message: 
+   pdb_handle_exception [0x88][0xfc5c9d88]
+   At this point Xen is waiting for gdb commands on the serial line.
+3. Attach with gdb
+   (gdb) file xeno.bk/xen/xen
+   Reading symbols from xeno.bk/xen/xen...done.
+   (gdb) target remote <hostname>:<port + 1>              /* contact nsplitd */
+   Remote debugging using serial.srg:12131
+   continue_cpu_idle_loop () at current.h:10
+   warning: shared library handler failed to enable breakpoint
+   (gdb) break __enter_scheduler
+   Breakpoint 1 at 0xfc510a94: file schedule.c, line 330.
+   (gdb) cont
+   Continuing.
+
+   Program received signal SIGTRAP, Trace/breakpoint trap.
+   __enter_scheduler () at schedule.c:330
+   (gdb) step
+   (gdb) step
+   (gdb) print next            /* the variable prev has been optimized away! */
+   $1 = (struct task_struct *) 0x0
+   (gdb) delete
+   Delete all breakpoints? (y or n) y
+4. You can add additional symbols to gdb
+   (gdb) add-sym xenolinux-2.4.24/vmlinux
+   add symbol table from file "xenolinux-2.4.24/vmlinux" at
+   (y or n) y
+   Reading symbols from xenolinux-2.4.24/vmlinux...done.
+   (gdb) x/s cpu_vendor_names[0]
+   0xc01530d2 <cpdext+62898>:   "Intel"
+   (gdb) break free_uid
+   Breakpoint 2 at 0xc0012250
+   (gdb) cont
+   Continuing.                                  /* run a command in domain 0 */
+
+   Program received signal SIGTRAP, Trace/breakpoint trap.
+   free_uid (up=0xbffff738) at user.c:77
+
+   (gdb) print *up
+   $2 = {__count = {counter = 0}, processes = {counter = 135190120}, files = {
+       counter = 0}, next = 0x395, pprev = 0xbffff878, uid = 134701041}
+   (gdb) finish
+   Run till exit from #0  free_uid (up=0xbffff738) at user.c:77
+
+   Program received signal SIGTRAP, Trace/breakpoint trap.
+   release_task (p=0xc2da0000) at exit.c:51
+   (gdb) print *p
+$3 = {state = 4, flags = 4, sigpending = 0, addr_limit = {seg = 3221225472}, 
+  exec_domain = 0xc016a040, need_resched = 0, ptrace = 0, lock_depth = -1, 
+  counter = 1, nice = 0, policy = 0, mm = 0x0, processor = 0, 
+  cpus_runnable = 1, cpus_allowed = 4294967295, run_list = {next = 0x0, 
+    prev = 0x0}, sleep_time = 18995, next_task = 0xc017c000, 
+  prev_task = 0xc2f94000, active_mm = 0x0, local_pages = {next = 0xc2da0054, 
+    prev = 0xc2da0054}, allocation_order = 0, nr_local_pages = 0, 
+  binfmt = 0xc016c6a0, exit_code = 0, exit_signal = 17, pdeath_signal = 0, 
+  personality = 0, did_exec = -1, task_dumpable = 1, pid = 917, pgrp = 914, 
+  tty_old_pgrp = 0, session = 914, tgid = 917, leader = 0, 
+  p_opptr = 0xc2f94000, p_pptr = 0xc2f94000, p_cptr = 0x0, p_ysptr = 0x0, 
+  p_osptr = 0x0, thread_group = {next = 0xc2da00a8, prev = 0xc2da00a8}, 
+  pidhash_next = 0x0, pidhash_pprev = 0xc01900b8, wait_chldexit = {
+    lock = <incomplete type>, task_list = {next = 0xc2da00b8, 
+      prev = 0xc2da00b8}}, vfork_done = 0x0, rt_priority = 0, 
+  it_real_value = 0, it_prof_value = 0, it_virt_value = 0, it_real_incr = 0, 
+  it_prof_incr = 0, it_virt_incr = 0, real_timer = {list = {next = 0x0, 
+      prev = 0x0}, expires = 18950, data = 3269066752, 
+    function = 0xc000ce30 <it_real_fn>}, times = {tms_utime = 0, 
+    tms_stime = 0, tms_cutime = 0, tms_cstime = 0}, start_time = 18989, 
+  per_cpu_utime = {1}, per_cpu_stime = {310}, min_flt = 13, maj_flt = 104, 
+  nswap = 0, cmin_flt = 0, cmaj_flt = 0, cnswap = 0, swappable = -1, uid = 0, 
+  euid = 0, suid = 0, fsuid = 0, gid = 0, egid = 0, sgid = 0, fsgid = 0, 
+  ngroups = 7, groups = {0, 1, 2, 3, 4, 6, 10, 0 <repeats 25 times>}, 
+  cap_effective = 4294967039, cap_inheritable = 0, cap_permitted = 4294967039, 
+  keep_capabilities = 0, user = 0xc016b18c, rlim = {{rlim_cur = 4294967295, 
+      rlim_max = 4294967295}, {rlim_cur = 4294967295, rlim_max = 4294967295}, {
+      rlim_cur = 4294967295, rlim_max = 4294967295}, {rlim_cur = 8388608, 
+      rlim_max = 4294967295}, {rlim_cur = 0, rlim_max = 4294967295}, {
+      rlim_cur = 4294967295, rlim_max = 4294967295}, {rlim_cur = 512, 
+      rlim_max = 512}, {rlim_cur = 1024, rlim_max = 1024}, {
+      rlim_cur = 4294967295, rlim_max = 4294967295}, {rlim_cur = 4294967295, 
+      rlim_max = 4294967295}, {rlim_cur = 4294967295, rlim_max = 4294967295}}, 
+  used_math = 0, comm = "id\000h\000og\000\000\000\000\000\000\000\000", 
+  link_count = 0, total_link_count = 1, tty = 0xc3ed1000, locks = 0, 
+  semundo = 0x0, semsleeping = 0x0, thread = {esp0 = 3269074944, 
+    eip = 3221249046, esp = 3269074792, fs = 0, gs = 0, io_pl = 3, debugreg = {
+      0, 0, 0, 0, 0, 0, 0, 0}, cr2 = 0, trap_no = 0, error_code = 0, i387 = {
+      fsave = {cwd = 2098047, swd = 125632512, twd = 1073944696, fip = 2091, 
+        fcs = -1073745032, foo = 2099, fos = 8064, st_space = {
+          0 <repeats 20 times>}, status = 0}, fxsave = {cwd = 895, swd = 32, 
+        twd = 0, fop = 1917, fip = 1073944696, fcs = 2091, foo = -1073745032, 
+        fos = 2099, mxcsr = 8064, reserved = 0, st_space = {
+          0 <repeats 24 times>, 1449431204, -1774489361, 16383, 0, 1, 
+          -1891252224, 16404, 0}, xmm_space = {0 <repeats 32 times>}, 
+        padding = {0 <repeats 56 times>}}, soft = {cwd = 2098047, 
+        swd = 125632512, twd = 1073944696, fip = 2091, fcs = -1073745032, 
+        foo = 2099, fos = 8064, st_space = {0 <repeats 20 times>}, 
+        ftop = 0 '\0', changed = 0 '\0', lookahead = 0 '\0', 
+        no_update = 0 '\0', rm = 0 '\0', alimit = 0 '\0', info = 0x0, 
+        entry_eip = 0}}, vm86_info = 0x0, screen_bitmap = 0, v86flags = 0, 
+    v86mask = 0, saved_esp0 = 0}, fs = 0x0, files = 0x0, namespace = 0x0, 
+  sigmask_lock = <incomplete type>, sig = 0x0, blocked = {sig = {0, 0}}, 
+  pending = {head = 0x0, tail = 0xc2da04f8, signal = {sig = {0, 0}}}, 
+  sas_ss_sp = 0, sas_ss_size = 0, notifier = 0, notifier_data = 0x0, 
+  notifier_mask = 0x0, parent_exec_id = 7, self_exec_id = 8, 
+  alloc_lock = <incomplete type>, journal_info = 0x0}
diff --git a/tools/nsplitd/Makefile b/tools/nsplitd/Makefile
new file mode 100644 (file)
index 0000000..4f59075
--- /dev/null
@@ -0,0 +1,11 @@
+ROOT   = ..
+
+CFILES = $(wildcard *.c)
+
+INSTALL_BINS := nsplitd
+
+include $(ROOT)/mk/rules.mk
+
+nsplitd: $(OBJS)
+       $(CC) $(CFLAGS) -o nsplitd $(OBJS)
+
diff --git a/tools/nsplitd/nsplitd.c b/tools/nsplitd/nsplitd.c
new file mode 100644 (file)
index 0000000..48fbd65
--- /dev/null
@@ -0,0 +1,686 @@
+/*
+ *     nsplitd.c
+ *     ---------
+ *
+ * $Id: nsplitd.c,v 2.6 1998/09/17 14:28:37 sde1000 Exp $
+ *
+ * Copyright (c) 1995, University of Cambridge Computer Laboratory,
+ * Copyright (c) 1995, Richard Black, All Rights Reserved.
+ *
+ *
+ * A complete re-implementation of DME's nsplitd for use from inetd
+ *
+ */
+
+/* The basic stream comes in (via inetd) and we then conenct to
+ * somewhere else providing a loop-through service, except we offer
+ * two other ports for connection - one of which gets a second channel
+ * using the top bit to distinguish, and the other is a master control
+ * port (normally used for gdb) which gets complete exclusive access
+ * for its duration.
+ *
+ * Originally designed for multiplexing a xwcons/telnet with a gdb
+ * post-mortem debugging session.
+ *
+ * Here is a picture:
+ *
+ *                                         port0 (from inetd)
+ *      8-bit connection                  /
+ *        made by us      <----> nsplitd <-----gdbport (default port0+2)
+ *     to host:port/tcp                  |\
+ *                                       | port1 (default port0+1)
+ *                                         \
+ *                                          control (default port0+3)
+ *
+ * If port1 is explicitly disabled (through a command-line option) then
+ * port0 becomes 8-bit clean.
+ */
+
+/*
+ * N.B.: We do NOT support 8 bit stdin/stdout usage on a
+ * /dev/... because to do that right involves much messing with ioctl
+ * and TIOC... etc.  If you want to do that sort of thing then the
+ * right way to do it is to chain this onto wconsd (which does know
+ * about and understand all the ioctl and TIOC grief).
+ */
+
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <string.h>
+
+#include <sys/time.h>
+#include <sys/signal.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <sys/ioctl.h>
+#include <syslog.h>
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef LOG_DAEMON
+#define LOG_DAEMON 0
+#endif
+
+#define DB(x)  /* ((x), fflush(stderr)) */
+
+extern char *optarg;
+
+extern int optind, opterr, optopt;
+
+static char *prog_name;
+
+static void usage(void)
+{
+    fprintf(stderr, "This program (%s) should be run via inetd (tcp)\n\n",
+           prog_name);
+    fprintf(stderr, "usage: %s [-h<highport>][-g<gdbport>]"
+           "[-c<ctlport>][-8] host:service\n",
+           prog_name);
+    exit(1);
+}
+
+static void fault(char *format, ...)
+{
+    va_list            ap;
+    char               logbuf[1024];
+
+    va_start(ap, format);
+    fprintf(stderr, "%s: ", prog_name);
+    vfprintf(stderr, format, ap);
+    fflush(stderr);
+    va_end(ap);
+    
+    /* XXX This is a bit dubious, but there is no vsyslog */
+    va_start(ap, format);
+    vsprintf(logbuf, format, ap);
+    syslog(LOG_ERR, logbuf);
+    va_end(ap);
+    exit(1);
+}
+
+static int getservice(char *name, unsigned short *port)
+{
+    struct servent             *se;
+
+    if (!name) return -1;
+
+    if (isdigit(name[0]))
+       *port = atoi(name);
+    else
+    {
+       if (!(se = getservbyname(name, "tcp")))
+           return -1;
+       *port = ntohs(se->s_port);
+    }
+    return 0;
+}
+
+/* 
+ *  connect_host: connect to ("name", "port")
+ */
+static int connect_host (char *name, unsigned int port)
+{
+    int                        fd;
+    struct hostent     *hostent;
+    struct sockaddr_in sin;
+    int                        on;
+    
+    if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
+       fault("socket");
+    
+    if (!(hostent = gethostbyname(name)))
+       fault("gethostbyname: %s: %s\n", name, strerror(errno));
+    
+    memset(&sin, 0, sizeof(sin));
+    sin.sin_family = AF_INET;
+    sin.sin_port   = htons (port);
+    memcpy(&sin.sin_addr.s_addr, hostent->h_addr, sizeof(struct in_addr));
+    
+    if (connect(fd, (struct sockaddr *) &sin, sizeof (sin)) < 0)
+       fault("connect: %s:%u: %s\n", name, port, strerror(errno));
+    
+    on = 1;
+    if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)) < 0)
+       syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m");
+
+    on = 1;
+    if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0)
+       syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
+
+    return fd;
+}
+
+/*
+ * open a tcp socket and start listening for connections on it
+ */
+static int startlistening(unsigned short port)
+{
+    int                        fd, on;
+    struct sockaddr_in sin;
+
+    if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
+       fault("socket");
+    
+    on = 1;
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0)
+      syslog(LOG_WARNING, "setsockopt (SO_REUSEADDR): %m");
+
+    memset(&sin, 0, sizeof(sin));
+    sin.sin_family      = AF_INET;
+    sin.sin_port        = htons (port);
+    sin.sin_addr.s_addr = INADDR_ANY;
+    if (bind(fd, &sin, sizeof(sin)) < 0)
+       fault("bind: %u: %s\n", port, strerror(errno));
+    
+    if (listen(fd, 1) < 0)
+       fault("listen: %s\n", strerror(errno));
+    
+    return fd;
+}
+
+static void noblock(int fd)
+{
+    int on=1;
+    
+    if (ioctl(fd, FIONBIO, &on) < 0)
+       fault("ioctl: FIONBIO: %s\n", strerror(errno));
+}
+
+
+/* You might not believe this, but fd_sets don't have to be a 32-bit
+ * integer.  In particular, in glibc2 it is an array of unsigned
+ * longs.  Hence, this hacked up FD_SET_rjb() that works out if it
+ * would have been a nop. */
+#define FD_SET_rjb(fd, setp) \
+do {                                           \
+    if ((fd) != 32)                            \
+       FD_SET((fd), (setp));                   \
+} while(0)
+
+#define FD_ISSET_rjb(fd, setp) (((fd) != 32)? FD_ISSET((fd), (setp)) : 0)
+
+#define MAXSIZE        256
+
+/* -----------------------------------------------------------------
+ * The main bit of the algorithm. Note we use 32 to mean not connected
+ * because this gives us 1<<32 == 0. We could have done this one
+ * character at a time, but that would have been very inefficient and
+ * not the unix way.  */
+static int debug;
+
+static void doit(int actl, int acto, int lish, int lisg, int lisc)
+{
+    int                acth, actg, actc;
+    int                gdbmode = FALSE;
+    char       gibuf[MAXSIZE], oibuf[MAXSIZE];
+    char       libuf[MAXSIZE], lobuf[MAXSIZE];
+    char       hibuf[MAXSIZE], hobuf[MAXSIZE];
+    char       ctlbuf[MAXSIZE];
+    fd_set     rdfs, wrfs, exfs;
+    int                gicc, oicc, licc, locc, hicc, hocc, ctlcc;
+    char       *giptr, *oiptr, *liptr, *loptr, *hiptr, *hoptr;
+    int                rc, fromlen;
+    struct sockaddr_in         from;
+    
+    gicc = oicc = licc = locc = hicc = hocc = ctlcc = 0;
+    acth = actg = actc = 32;                   /* XXX yummy */
+
+    noblock(actl);
+    noblock(acto);
+
+    for(;;)
+    {
+       FD_ZERO(&rdfs);
+       FD_ZERO(&wrfs);
+       FD_ZERO(&exfs);
+
+       /* always take input from the control port (if it's connected) */
+       FD_SET_rjb(actc, &rdfs);
+
+       if (gdbmode)
+       {
+           if (oicc)
+               FD_SET_rjb(actg, &wrfs);
+           else
+               FD_SET_rjb(acto, &rdfs);
+           
+           if (gicc)
+               FD_SET_rjb(acto, &wrfs);
+           else
+               FD_SET_rjb(actg, &rdfs);
+       }
+       else
+       {
+           /* There is no such thing as oibuf because its been split into
+            * lobuf and hobuf
+            */
+           if (locc || hocc)
+           {
+               if (locc)
+                   FD_SET_rjb(actl, &wrfs);
+               if (hocc)
+                   FD_SET_rjb(acth, &wrfs);
+           }
+           else
+               FD_SET_rjb(acto, &rdfs);
+           
+           if (licc)
+               FD_SET_rjb(acto, &wrfs);
+           else
+               FD_SET_rjb(actl, &rdfs);
+           
+           if (hicc)
+               FD_SET_rjb(acto, &wrfs);
+           else
+               FD_SET_rjb(acth, &rdfs);
+       }
+       
+       if (acth == 32 && lish>=0)      FD_SET_rjb(lish, &rdfs);
+       if (actg == 32)                 FD_SET_rjb(lisg, &rdfs);
+       if (actc == 32)                 FD_SET_rjb(lisc, &rdfs);
+
+       /* now make exfs the union of the read and write fd sets, plus
+        * "actl" */
+       {
+           int i;
+           exfs = rdfs;
+           for(i=0; i<32; i++)  /* XXX we only copy fd numbers up to 31 */
+               if (FD_ISSET(i, &wrfs))
+                   FD_SET_rjb(i, &exfs);
+           FD_SET_rjb(actl, &exfs);
+       }
+
+       /* XXX AND: can't print something of type fd_set as %x - it
+         * might be an array */
+       DB(fprintf(stderr, "%s: before select: %08x %08x %08x\n",
+                  prog_name, rdfs, wrfs, exfs));
+       
+       if (select(32, &rdfs, &wrfs, &exfs, NULL) < 0)
+           fault("select: %s\n", strerror(errno));
+       
+       DB(fprintf(stderr, "%s: after  select: %08x %08x %08x\n",
+                  prog_name, rdfs, wrfs, exfs));
+       
+       /* XXX it appears that a non-blocking socket may not show up
+        * correctly in exfs but instead goes readable with no data in
+        * it. Thus we check for zero and goto the appropriate close
+        * method.  */
+
+       /* Deal with exceptions */
+       if (FD_ISSET_rjb(actg, &exfs))
+       {
+       exfs_actg:
+           close(actg);
+           gdbmode = FALSE;
+           oicc = 0;
+           oiptr = oibuf;
+           actg = 32;
+           continue;           /* because assumptions changed */
+       }
+       if (FD_ISSET_rjb(acth, &exfs))
+       {
+       exfs_acth:
+           close(acth);
+           hicc = hocc = 0;
+           hiptr = hibuf;
+           hoptr = hibuf;
+           acth = 32;
+           continue;           /* because assumptions changed */
+       }
+       if (FD_ISSET_rjb(actl, &exfs) ||
+           FD_ISSET_rjb(acto, &exfs))
+       {
+       exfs_actl:
+       exfs_acto:
+           /* Thats all folks ... */
+           break;
+       }
+       if (FD_ISSET_rjb(actc, &exfs))
+       {
+       exfs_ctl:
+           close(actc);
+           actc = 32;
+           ctlcc = 0;
+           continue;
+       }
+
+       /* Deal with reading */
+       if (FD_ISSET_rjb(acto, &rdfs))
+       {
+           if ((oicc = read(acto, oiptr = oibuf, MAXSIZE)) < 0)
+               fault("read acto: %d: %s\n", oicc, strerror(errno));
+           if (!oicc) goto exfs_acto;
+           
+           if (!gdbmode)
+           {
+               int t;
+
+               assert((locc == 0) && (hocc == 0));
+               loptr = lobuf;
+               hoptr = hobuf;
+               
+               if (lish>=0) {
+                   for(t=0; t<oicc; t++)
+                       if (oibuf[t] & 0x80)
+                           hobuf[hocc++] = oibuf[t] & 0x7f;
+                       else
+                           lobuf[locc++] = oibuf[t];
+               } else {
+                   for (t=0; t<oicc; t++)
+                       lobuf[locc++] = oibuf[t];
+               }
+               /* If no high connection scratch that */
+               if (acth == 32)
+                   hocc=0;
+           }
+       }
+       if (FD_ISSET_rjb(actl, &rdfs))
+       {
+           if ((licc = read(actl, liptr = libuf, MAXSIZE)) < 0)
+               fault("read actl: %d: %s\n", licc, strerror(errno));
+           if (!licc) goto exfs_actl;
+       }
+       if (FD_ISSET_rjb(acth, &rdfs))
+       {
+           int t;
+           
+           if ((hicc = read(acth, hiptr = hibuf, MAXSIZE)) < 0)
+               fault("read acth: %d: %s\n", hicc, strerror(errno));
+           if (!hicc) goto exfs_acth;
+           for(t=0; t<hicc; t++)
+               hibuf[t] |= 0x80;
+       }
+       if (FD_ISSET_rjb(actg, &rdfs))
+       {
+           if ((gicc = read(actg, giptr = gibuf, MAXSIZE)) < 0)
+               fault("read actg: %d: %s\n", gicc, strerror(errno));
+           if (debug) write(1, giptr, gicc);           /* XXX */
+           if (!gicc) goto exfs_actg;
+       }
+       if (FD_ISSET_rjb(actc, &rdfs))
+       {
+           if ((ctlcc = read(actc, ctlbuf, MAXSIZE)) < 0)
+               fault("read actc: %d: %s\n", ctlcc, strerror(errno));
+           if (debug) write(1, ctlbuf, gicc);
+           if (!ctlcc) goto exfs_ctl;
+           if (ctlbuf[0] == 'r') /* reset command */
+           {
+               syslog(LOG_INFO, "reset command read, exiting");
+               if (debug) write(1, "reseting\n", sizeof("reseting\n"));
+               break;
+           }
+       }
+       
+       /* Deal with writing */
+       if (FD_ISSET_rjb(actg, &wrfs))
+       {
+           /* We must be in gdb mode so send oi buffer data */
+           assert(gdbmode);
+           if (debug) write(2, oiptr, oicc);           /* XXX */
+           if ((rc = write(actg, oiptr, oicc)) <= 0)
+               fault("write actg: %d: %s\n", rc, strerror(errno));
+           oiptr += rc;
+           oicc  -= rc;
+       }
+       if (FD_ISSET_rjb(actl, &wrfs))
+       {
+           if ((rc = write(actl, loptr, locc)) <= 0)
+               fault("write actl: %d: %s\n", rc, strerror(errno));
+           loptr += rc;
+           locc  -= rc;
+       }
+       if (FD_ISSET_rjb(acth, &wrfs))
+       {
+           if ((rc = write(acth, hoptr, hocc)) <= 0)
+               fault("write acth: %d: %s\n", rc, strerror(errno));
+           hoptr += rc;
+           hocc  -= rc;
+       }
+       if (FD_ISSET_rjb(acto, &wrfs))
+       {
+           /* If in gdb mode send gdb input, otherwise send low data
+              preferentially */
+           if (gdbmode)
+           {
+               assert(gicc);
+               if ((rc = write(acto, giptr, gicc)) <= 0)
+                   fault("write acto: %d: %s\n", rc, strerror(errno));
+               giptr += rc;
+               gicc  -= rc;
+           }
+           else
+           {
+               if (licc)
+               {
+                   if ((rc = write(acto, liptr, licc)) <= 0)
+                       fault("write acto: %d: %s\n", rc, strerror(errno));
+                   liptr += rc;
+                   licc  -= rc;
+               }
+               else
+               {
+                   assert(hicc);
+                   if ((rc = write(acto, hiptr, hicc)) <= 0)
+                       fault("write acto: %d: %s\n", rc, strerror(errno));
+                   hiptr += rc;
+                   hicc  -= rc;
+               }
+           }
+       }
+       
+       /* Deals with new connections */
+       if ((acth == 32) && lish>=0 && (FD_ISSET_rjb(lish, &rdfs)))
+       {
+           fromlen = sizeof(from);
+           if ((acth = accept(lish, &from, &fromlen)) < 0)
+           {
+               syslog(LOG_WARNING, "accept: %m");
+               acth = 32;
+           }
+           else
+           {
+               noblock(acth);
+               hicc = hocc = 0;
+               syslog(LOG_INFO, "highbit client peer is %s:%u\n",
+                      inet_ntoa(from.sin_addr), ntohs(from.sin_port));
+           }
+       }
+       
+       if ((actg == 32) && (FD_ISSET_rjb(lisg, &rdfs)))
+       {
+           fromlen = sizeof(from);
+           if ((actg = accept(lisg, &from, &fromlen)) < 0)
+           {
+               syslog(LOG_WARNING, "accept: %m");
+               actg = 32;
+           }
+           else
+           {
+               noblock(actg);
+               gicc = 0;
+               gdbmode = TRUE;
+               syslog(LOG_INFO, "gdb client peer is %s:%u\n",
+                      inet_ntoa(from.sin_addr), ntohs(from.sin_port));
+           }
+       }
+
+       if ((actc == 32) && (FD_ISSET_rjb(lisc, &rdfs)))
+       {
+           fromlen = sizeof(from);
+           if ((actc = accept(lisc, &from, &fromlen)) < 0)
+           {
+               syslog(LOG_WARNING, "accept (ctl): %m");
+               actc = 32;
+           }
+           else
+           {
+               noblock(actc);
+               syslog(LOG_INFO, "ctl client peer is %s:%u\n",
+                      inet_ntoa(from.sin_addr), ntohs(from.sin_port));
+           }
+       }
+           
+       /* Back to top of loop */
+    }
+    
+    /* We are bailing because one of the primary connections has gone
+     * away. We close these all explicitly here because that way the
+     * timeout on reusing the port numbers is smnaller. */
+    
+    close(acth);
+    close(actg);
+    /* XXX AND: why are we closing all these "character counts" ?? */
+    close(gicc);
+    close(oicc);
+    close(licc);
+    close(locc);
+    close(hicc);
+    close(hocc);
+}
+
+/*
+ * ------------------------------------------------------------
+ */
+int main(int argc, char **argv)
+{
+    /* In general, suffix "l" is low channel, "h" is high channel, "g"
+     * is gdb channel, "c" is control channel and "o" is output channel.
+     */
+    struct sockaddr_in         from;
+    int                                infd = 0, outfd;
+    unsigned short             portl, porth, portg, portc, porto;
+    int                                on = 1, c;
+    char                       *outname, *outservice;
+    int                                fromlen;
+    int                                lish, lisg, lisc;
+#if 0
+    FILE                       *newerr;
+#endif /* 0 */
+    
+    prog_name = argv[0];
+
+    if (isatty(infd))
+       usage();
+
+    /* Here, then not just a simple idiot. */
+
+    signal(SIGPIPE, SIG_IGN);
+
+    openlog(prog_name, LOG_PID, LOG_DAEMON);
+
+    fromlen = sizeof(from);
+    if (getsockname(infd, &from, &fromlen) < 0)
+       fault("getsockname: %s", strerror(errno));
+    if ((fromlen != sizeof(from)) || (from.sin_family != AF_INET))
+       fault("not an inet socket (family=%d)\n", from.sin_family);
+    
+    portl = ntohs(from.sin_port);
+    porth = portl+1;
+    portg = porth+1;
+    portc = portg+1;
+
+    fromlen = sizeof(from);
+    if (getpeername(infd, &from, &fromlen) < 0)
+       fault("getpeername: %s", strerror(errno));
+    if ((fromlen != sizeof(from)) || (from.sin_family != AF_INET))
+       fault("not an inet socket (family=%d)\n", from.sin_family);
+
+    syslog(LOG_INFO, "on port %u peer is %s:%u\n", portl,
+          inet_ntoa(from.sin_addr), ntohs(from.sin_port));
+    
+    if (setsockopt(infd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0)
+       syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
+
+    /* from here on, we map stderr to output on the connection so we can
+     * report errors to the remote user.
+     */
+#if 0
+    if (!(newerr = fdopen(infd, "w")))
+       syslog(LOG_WARNING, "fdopen: %m");
+    else
+       *stderr = *newerr;
+#endif
+       
+    while((c = getopt(argc, argv, "d8h:g:c:")) != EOF)
+    {
+       switch(c)
+       {
+       case 'd':
+           debug++;
+           break;
+           
+       case 'h':
+           /* high bit port */
+           if (getservice(optarg, &porth) < 0)
+               fault("getservice failed (high port '%s')\n", optarg);
+           break;
+           
+       case 'g':
+           /* gdb port */
+           if (getservice(optarg, &portg) < 0)
+               fault("getservice failed (gdb port '%s')\n", optarg);
+           break;
+
+       case 'c':
+           /* control port */
+           if (getservice(optarg, &portc) < 0)
+               fault("getservice failed (control port '%s')\n", optarg);
+           break;
+
+       case '8':
+           /* 8-bit clean; no high port */
+           porth=0;
+           break;
+
+       default:
+           fault("bad argument list!\n");
+       }
+    }
+    
+    if (argc != optind + 1)
+       fault("unparsed arguments (%d!=%d)\n", argc, optind+1);
+
+    outname = argv[optind];
+    if (!(outservice = strchr(outname, ':')))
+       fault("output arg '%s' doesn't contain ':'\n", outname);
+    *outservice++ = 0;
+    if (getservice(outservice, &porto) < 0)
+       fault("getservice failed (output port '%s')\n", outservice);
+    
+    /* Time to start the sockets */
+
+    if (porth) {
+       lish  = startlistening(porth);
+    } else {
+       lish  = -1;
+    }
+    lisg  = startlistening(portg);
+    lisc  = startlistening(portc);
+    
+    outfd = connect_host(outname, porto);
+    
+    doit(infd, outfd, lish, lisg, lisc);
+
+    syslog(LOG_INFO, "terminating normally\n");
+
+    fclose(stderr);
+
+    closelog();
+    exit(0); 
+}
+
+/* End $Id: nsplitd.c,v 2.6 1998/09/17 14:28:37 sde1000 Exp $ */
diff --git a/xen/arch/i386/pdb-stub.c b/xen/arch/i386/pdb-stub.c
new file mode 100644 (file)
index 0000000..7734f2f
--- /dev/null
@@ -0,0 +1,831 @@
+#include <xeno/lib.h>
+#include <xeno/sched.h>
+#include <asm-i386/ptrace.h>
+#include <xeno/keyhandler.h> 
+#include <asm/pdb.h>
+#include <xeno/list.h>
+
+#undef DEBUG_TRACE
+#ifdef DEBUG_TRACE
+#define TRC(_x) _x
+#else
+#define TRC(_x)
+#endif
+
+#define BUFMAX 400
+
+#define PDB_DOMAIN_OFFSET 2              /* all domains are positive numbers */
+
+static const char hexchars[]="0123456789abcdef";
+
+int remote_debug;
+
+int pdb_foobar = 0x123456;                                        /* testing */
+char *pdb_foobaz = "cambridge";                                   /* testing */
+
+#define PDB_BUFMAX 1024
+static char pdb_in_buffer[PDB_BUFMAX];
+static char pdb_out_buffer[PDB_BUFMAX];
+static char pdb_buffer[PDB_BUFMAX];
+static int  pdb_in_buffer_ptr;
+static unsigned char  pdb_in_checksum;
+static unsigned char  pdb_xmit_checksum;
+
+int pdb_ctrl_thread = -1;
+int pdb_info_thread = -1;
+int pdb_stepping = 0;
+
+int   hex (char);
+char *mem2hex (char *, char *, int);
+char *hex2mem (char *, char *, int);
+int   hexToInt (char **ptr, int *intValue);
+
+void pdb_put_packet (unsigned char *buffer, int ack);
+void pdb_put_char (u_char c);
+u_char pdb_get_char ();
+
+static volatile int mem_err = 0;
+void set_mem_err (void)                                   /* NOT USED YET... */
+{
+  mem_err = 1;
+}
+
+/* These are separate functions so that they are so short and sweet
+   that the compiler won't save any registers (if there is a fault
+   to mem_fault, they won't get restored, so there better not be any
+   saved).  */
+int
+get_char (char *addr)
+{
+    return *addr;
+}
+
+void
+set_char (char *addr, int val)
+{
+    *addr = val;
+}
+
+void
+pdb_process_query (char *ptr)
+{
+    if (strcmp(ptr, "C") == 0)
+    {
+        /* empty string */
+    }
+    else if (strcmp(ptr, "fThreadInfo") == 0)
+    {
+        struct task_struct *p = &idle0_task;
+        u_long flags;
+       int count = 0, buf_idx = 0;
+
+        read_lock_irqsave (&tasklist_lock, flags);
+
+       pdb_out_buffer[buf_idx++] = 'm';
+        while ( (p = p->next_task) != &idle0_task )
+       {
+           int domain = p->domain + PDB_DOMAIN_OFFSET;
+
+           if (count > 0)
+               pdb_out_buffer[buf_idx++] = ',';
+           /*
+           if (domain < 0)
+           {   pdb_out_buffer[buf_idx++] = '-'; domain = domain * -1; }
+           */
+           if (domain > 15)
+           {
+               pdb_out_buffer[buf_idx++] = hexchars[domain >> 4];
+           }
+           pdb_out_buffer[buf_idx++] = hexchars[domain % 16];
+           count++;
+       }
+       pdb_out_buffer[buf_idx++] = 'l';
+       pdb_out_buffer[buf_idx++] = 0;
+
+        read_unlock_irqrestore(&tasklist_lock, flags);
+    }
+    else if (strcmp(ptr, "sThreadInfo") == 0)
+    {
+    }
+    else if (strncmp(ptr, "ThreadExtraInfo,", 16) == 0)
+    {
+        int thread = 0;
+       char *message = "whatever!";
+
+       ptr += 16;
+        if (hexToInt (&ptr, &thread))
+       {
+         mem2hex ((char *)message, pdb_out_buffer, strlen(message) + 1);
+       }
+    }
+    else if (strcmp(ptr, "Offsets") == 0)
+    {
+        /* empty string */
+    }
+    else if (strncmp(ptr, "Symbol", 6) == 0)
+    {
+        strcpy (pdb_out_buffer, "OK");
+    }
+    else
+    {
+        printk("pdb_process_query: unknown query [%s]\n", ptr);
+    }
+}
+
+int
+pdb_process_command (char *ptr, struct pt_regs *regs)
+{
+    int sigval = 10;
+    int length;
+    unsigned long addr;
+    int ack = 1;                           /* wait for ack in pdb_put_packet */
+    int go = 0;
+
+    TRC(printk("pdb: [%s]\n", ptr));
+    {
+        pdb_out_buffer[0] = 0;
+
+       switch (*ptr++)
+       {
+       case '?':
+          pdb_out_buffer[0] = 'S';
+          pdb_out_buffer[1] = hexchars[sigval >> 4];
+          pdb_out_buffer[2] = hexchars[sigval % 16];
+          pdb_out_buffer[3] = 0;
+         break;
+        case 'S':                                        /* step with signal */
+        case 's':                                                    /* step */
+         regs->eflags |= 0x100;
+         pdb_stepping = 1;
+         return 1;                                        
+         /* not reached */
+        case 'C':                                    /* continue with signal */
+        case 'c':                                                /* continue */
+         regs->eflags &= ~0x100;
+         /* jump out before replying to gdb */
+         return 1;
+         /* not reached */
+       case 'd':
+         remote_debug = !(remote_debug);               /* toggle debug flag */
+         break;
+       case 'D':                                                  /* detach */
+         return go;
+         /* not reached */
+       case 'g':                   /* return the value of the CPU registers */
+       {
+           int idx = 0;
+           mem2hex ((char *)&regs->eax, &pdb_out_buffer[idx], sizeof(regs->eax));
+           idx += sizeof(regs->eax) * 2;
+           mem2hex ((char *)&regs->ecx, &pdb_out_buffer[idx], sizeof(regs->ecx));
+           idx += sizeof(regs->ecx) * 2;
+           mem2hex ((char *)&regs->edx, &pdb_out_buffer[idx], sizeof(regs->edx));
+           idx += sizeof(regs->edx) * 2;
+           mem2hex ((char *)&regs->ebx, &pdb_out_buffer[idx], sizeof(regs->ebx));
+           idx += sizeof(regs->ebx) * 2;
+           mem2hex ((char *)&regs->esp, &pdb_out_buffer[idx], sizeof(regs->esp));
+           idx += sizeof(regs->esp) * 2;
+           mem2hex ((char *)&regs->ebp, &pdb_out_buffer[idx], sizeof(regs->ebp));
+           idx += sizeof(regs->ebp) * 2;
+           mem2hex ((char *)&regs->esi, &pdb_out_buffer[idx], sizeof(regs->esi));
+           idx += sizeof(regs->esi) * 2;
+           mem2hex ((char *)&regs->edi, &pdb_out_buffer[idx], sizeof(regs->edi));
+           idx += sizeof(regs->edi) * 2;
+           mem2hex ((char *)&regs->eip, &pdb_out_buffer[idx], sizeof(regs->eip));
+           idx += sizeof(regs->eip) * 2;
+           mem2hex ((char *)&regs->eflags, &pdb_out_buffer[idx], sizeof(regs->eflags));
+           idx += sizeof(regs->eflags) * 2;
+           mem2hex ((char *)&regs->xcs, &pdb_out_buffer[idx], sizeof(regs->xcs));
+           idx += sizeof(regs->xcs) * 2;
+           mem2hex ((char *)&regs->xss, &pdb_out_buffer[idx], sizeof(regs->xss));
+           idx += sizeof(regs->xss) * 2;
+           mem2hex ((char *)&regs->xds, &pdb_out_buffer[idx], sizeof(regs->xds));
+           idx += sizeof(regs->xds) * 2;
+           mem2hex ((char *)&regs->xes, &pdb_out_buffer[idx], sizeof(regs->xes));
+           idx += sizeof(regs->xes) * 2;
+           mem2hex ((char *)&regs->xfs, &pdb_out_buffer[idx], sizeof(regs->xfs));
+           idx += sizeof(regs->xfs) * 2;
+           mem2hex ((char *)&regs->xgs, &pdb_out_buffer[idx], sizeof(regs->xgs));
+
+           /*
+           TRC(printk ("  reg: %s \n", pdb_out_buffer));
+           TRC(printk ("  ebx: 0x%08lx\n", regs->ebx));
+           TRC(printk ("  ecx: 0x%08lx\n", regs->ecx));
+           TRC(printk ("  edx: 0x%08lx\n", regs->edx));
+           TRC(printk ("  esi: 0x%08lx\n", regs->esi));
+           TRC(printk ("  edi: 0x%08lx\n", regs->edi));
+           TRC(printk ("  ebp: 0x%08lx\n", regs->ebp));
+           TRC(printk ("  eax: 0x%08lx\n", regs->eax));
+           TRC(printk ("  xds: 0x%08x\n", regs->xds));
+           TRC(printk ("  xes: 0x%08x\n", regs->xes));
+           TRC(printk ("  xfs: 0x%08x\n", regs->xfs));
+           TRC(printk ("  xgs: 0x%08x\n", regs->xgs));
+           TRC(printk ("  eip: 0x%08lx\n", regs->eip));
+           TRC(printk ("  xcs: 0x%08x\n", regs->xcs));
+           TRC(printk ("  efl: 0x%08lx\n", regs->eflags));
+           TRC(printk ("  esp: 0x%08lx\n", regs->esp));
+           TRC(printk ("  xss: 0x%08x\n", regs->xss));
+           */
+
+           break;
+       }
+       case 'G':          /* set the value of the CPU registers - return OK */
+           break;
+
+       case 'H':
+       {
+           int thread;
+           char *next = &ptr[1];
+           if (hexToInt (&next, &thread))
+           {
+               if (thread > 0)
+               {
+                   thread = thread - PDB_DOMAIN_OFFSET;
+               }
+               if (*ptr == 'c')
+               {
+                   pdb_ctrl_thread = thread;
+               }
+               else if (*ptr == 'g')
+               {
+                   pdb_info_thread = thread;
+               }
+               else
+               {
+                   printk ("ack, unknown command %c (thread: %d)\n", 
+                           *ptr, thread);
+               }
+           }
+           strcpy (pdb_out_buffer, "OK");
+           break;
+       }
+       case 'k':                                            /* kill request */
+       {
+           strcpy (pdb_out_buffer, "OK");                    /* ack for fun */
+           printk ("don't kill bill...\n");
+           ack = 0;
+           break;
+       }
+
+       case 'q':
+       {
+           pdb_process_query(ptr);
+           break;
+       }
+
+       /* mAA..AA,LLLL  Read LLLL bytes at address AA..AA */
+       case 'm':
+       {
+           /* TRY TO READ %x,%x.  IF SUCCEED, SET PTR = 0 */
+           if (hexToInt (&ptr, (int *)&addr))
+               if (*(ptr++) == ',')
+                   if (hexToInt (&ptr, &length))
+                   {
+                       ptr = 0;
+                       mem_err = 0;
+
+                       if (pdb_info_thread >= 0)
+                       {
+                           pdb_get_values(pdb_info_thread, pdb_buffer, addr, length);
+                           mem2hex (pdb_buffer, pdb_out_buffer, length);
+                       }
+                       else
+                           mem2hex ((char *) addr, pdb_out_buffer, length); 
+                       if (mem_err)
+                       {
+                           strcpy (pdb_out_buffer, "E03");
+                       }
+                   }
+           
+           if (ptr)
+           {
+             strcpy (pdb_out_buffer, "E01");
+           }
+           break;
+       }
+
+       /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
+       case 'M':
+       {
+           /* TRY TO READ '%x,%x:'.  IF SUCCEED, SET PTR = 0 */
+           if (hexToInt (&ptr, (int *)&addr))
+               if (*(ptr++) == ',')
+                   if (hexToInt (&ptr, &length))
+                       if (*(ptr++) == ':')
+                       {
+                           mem_err = 0;
+
+                           pdb_set_values(pdb_info_thread, 
+                                          ptr, addr, length);
+
+                           if (mem_err)
+                           {
+                               strcpy (pdb_out_buffer, "E03");
+                           }
+                           else
+                           {
+                               strcpy (pdb_out_buffer, "OK");
+                           }
+
+                           ptr = 0;
+                       }
+           if (ptr)
+           {
+               strcpy (pdb_out_buffer, "E02");
+           }
+           break;
+       }
+       case 'T':
+       {
+           int thread;
+           if (hexToInt (&ptr, &thread))
+           {
+               thread -= PDB_DOMAIN_OFFSET;
+               struct task_struct *p = find_domain_by_id(thread);
+               if (p == NULL)
+               {
+                   strcpy (pdb_out_buffer, "E00");
+               }
+               else
+               {
+                   strcpy (pdb_out_buffer, "OK");
+               }
+               put_task_struct(p);
+           }
+           break;
+       }
+       }                                                          /* switch */
+
+       /* reply to the request */
+       pdb_put_packet (pdb_out_buffer, ack);
+    }
+
+    return go;
+}
+
+/*
+ * process an input character from the serial line.
+ *
+ * return "1" if the character is a gdb debug string
+ * (and hence shouldn't be further processed).
+ */
+
+int pdb_debug_state = 0;                /* small parser state machine */
+
+int pdb_serial_input(u_char c, struct pt_regs *regs)
+{
+    int out = 1;
+    int loop, count;
+
+    switch (pdb_debug_state)
+    {
+    case 0:                         /* not currently processing debug string */
+        if ( c == '$' )                                      /* start token */
+       {
+           pdb_debug_state = 1;
+           pdb_in_buffer_ptr = 0;
+           pdb_in_checksum = 0;
+           pdb_xmit_checksum = 0;
+       }
+       else 
+       {
+           out = 0;
+       }
+       break;
+    case 1:                                                       /* saw '$' */
+        if ( c == '#' )                                    /* checksum token */
+       {
+           pdb_debug_state = 2;
+           pdb_in_buffer[pdb_in_buffer_ptr] = 0;
+       }
+       else
+       {
+           pdb_in_checksum += c;
+           pdb_in_buffer[pdb_in_buffer_ptr++] = c;
+       }
+       break;
+    case 2:                                            /* 1st checksum digit */
+        pdb_xmit_checksum = hex(c) << 4;
+       pdb_debug_state = 3;
+       break;
+    case 3:                                            /* 2nd checksum digit */
+        pdb_xmit_checksum += hex(c);
+       if (pdb_in_checksum != pdb_xmit_checksum) 
+       {
+           pdb_put_char('-');                           /* checksum failure */
+           printk ("checksum failure [%s.%02x.%02x]\n", pdb_in_buffer,
+                   pdb_in_checksum, pdb_xmit_checksum);
+       }
+       else 
+       {
+           pdb_put_char('+');                              /* checksum okay */
+           if ( pdb_in_buffer_ptr > 1 && pdb_in_buffer[2] == ':' ) 
+           {
+               pdb_put_char(pdb_in_buffer[0]);
+               pdb_put_char(pdb_in_buffer[1]);
+               /* remove sequence chars from buffer */
+               count = strlen(pdb_in_buffer);
+               for (loop = 3; loop < count; loop++)
+                   pdb_in_buffer[loop - 3] = pdb_in_buffer[loop];
+           }
+
+           pdb_process_command (pdb_in_buffer, regs);
+       }
+       pdb_debug_state = 0;
+       break;
+    }
+
+    return out;
+}
+
+int hex(char ch)
+{
+  if ((ch >= 'a') && (ch <= 'f')) return (ch-'a'+10);
+  if ((ch >= '0') && (ch <= '9')) return (ch-'0');
+  if ((ch >= 'A') && (ch <= 'F')) return (ch-'A'+10);
+  return (-1);
+}
+
+/* convert the memory pointed to by mem into hex, placing result in buf */
+/* return a pointer to the last char put in buf (null) */
+char *
+mem2hex (mem, buf, count)
+     char *mem;
+     char *buf;
+     int count;
+{
+  int i;
+  unsigned char ch;
+
+  for (i = 0; i < count; i++)
+    {
+      ch = get_char (mem++);
+      *buf++ = hexchars[ch >> 4];
+      *buf++ = hexchars[ch % 16];
+    }
+  *buf = 0;
+  return (buf);
+}
+
+/* convert the hex array pointed to by buf into binary to be placed in mem */
+/* return a pointer to the character AFTER the last byte written */
+char *
+hex2mem (buf, mem, count)
+     char *buf;
+     char *mem;
+     int count;
+{
+  int i;
+  unsigned char ch;
+
+  for (i = 0; i < count; i++)
+    {
+      ch = hex (*buf++) << 4;
+      ch = ch + hex (*buf++);
+      set_char (mem++, ch);
+    }
+  return (mem);
+}
+
+int
+hexToInt (char **ptr, int *intValue)
+{
+  int numChars = 0;
+  int hexValue;
+  int negative = 0;
+
+  *intValue = 0;
+
+  if (**ptr == '-')
+  {
+    negative = 1;
+    numChars++;
+    (*ptr)++;
+  }
+  while (**ptr)
+  {
+      hexValue = hex (**ptr);
+      if (hexValue >= 0)
+      {
+          *intValue = (*intValue << 4) | hexValue;
+          numChars++;
+      }
+      else
+        break;
+
+      (*ptr)++;
+  }
+  if (negative)
+  {
+      *intValue *= -1;
+  }
+  
+  return (numChars);
+}
+
+/***********************************************************************/
+/***********************************************************************/
+
+
+/*
+ * Add a breakpoint to the list of known breakpoints.
+ * For now there should only be two or three breakpoints so
+ * we use a simple linked list.  In the future, maybe a red-black tree?
+ */
+struct pdb_breakpoint breakpoints;
+
+
+void pdb_bkpt_add (unsigned long address)
+{
+    struct pdb_breakpoint *bkpt;
+
+    bkpt = kmalloc(sizeof(struct pdb_breakpoint), GFP_KERNEL);
+    INIT_LIST_HEAD(&bkpt->list);
+
+    bkpt->address = address;
+
+    list_add(&bkpt->list, &breakpoints.list);
+
+    return;
+}
+
+/*
+ * Check to see of the breakpoint is in the list of known breakpoints 
+ *
+ * return 1 if it has been set, 0 otherwise
+ */
+
+struct pdb_breakpoint* pdb_bkpt_search (unsigned long address)
+{
+    struct pdb_breakpoint *found = NULL;
+    struct list_head *list_entry;
+    struct pdb_breakpoint *bkpt;
+
+    list_for_each(list_entry, &breakpoints.list)
+    {
+        bkpt = list_entry(list_entry, struct pdb_breakpoint, list);
+
+       if (bkpt->address == address)
+       {
+           found = bkpt;
+           break;
+       }
+    }
+
+    return found;
+}
+
+/*
+ * Remove a breakpoint to the list of known breakpoints.
+ *
+ * Return 1 if the element was not found, otherwise 0.
+ */
+
+void pdb_bkpt_remove_ptr (struct pdb_breakpoint *bkpt)
+{
+    struct list_head *list_entry = &bkpt->list;
+    list_del(list_entry);
+    kfree(bkpt);
+}
+
+int pdb_bkpt_remove (unsigned long address)
+{
+    struct list_head *list_entry;
+    struct pdb_breakpoint *bkpt;
+    int found = 1;
+
+    list_for_each(list_entry, &breakpoints.list)
+    {
+        bkpt = list_entry(list_entry, struct pdb_breakpoint, list);
+
+       if (bkpt->address == address)
+       {
+           pdb_bkpt_remove_ptr (bkpt);
+           found = 0;
+           break;
+       }
+    }
+
+    return found;
+}
+
+/***********************************************************************/
+
+void breakpoint(void);
+
+int pdb_initialized = 0;
+int pdb_high_bit = 1;
+
+void pdb_put_char (u_char c)
+{
+  extern void   debug_putchar(u_char);
+  u_char cc = pdb_high_bit ? c | 0x80 : c;
+  debug_putchar(cc);
+}
+
+u_char pdb_get_char ()
+{
+  extern u_char debug_getchar();
+  u_char cc = debug_getchar();
+  return cc & 0x7f;
+}
+
+/* send the packet in buffer.  */
+void pdb_put_packet (unsigned char *buffer, int ack)
+{
+    unsigned char checksum;
+    int count;
+    char ch;
+    
+    /*  $<packet info>#<checksum> */
+    /*  do */
+    {
+        pdb_put_char ('$');
+       checksum = 0;
+       count = 0;
+
+       while ((ch = buffer[count]))
+       {
+            pdb_put_char (ch);
+           checksum += ch;
+           count += 1;
+        }
+
+       pdb_put_char('#');
+       pdb_put_char(hexchars[checksum >> 4]);
+       pdb_put_char(hexchars[checksum % 16]);
+    }
+
+    if (ack)
+    {
+       if ((ch = pdb_get_char()) != '+')
+       {
+           printk(" pdb return error: %c 0x%x [%s]\n", ch, ch, buffer);
+       }
+    }
+}
+
+void pdb_get_packet(char *buffer)
+{
+    int count;
+    char ch;
+    unsigned char checksum = 0;
+    unsigned char xmitcsum = 0;
+
+    do
+    {
+        while ((ch = pdb_get_char()) != '$');
+
+       count = 0;
+       checksum = 0;
+
+       while (count < BUFMAX)
+       {
+           ch = pdb_get_char();
+           if (ch  == '#') break;
+           checksum += ch;
+           buffer[count] = ch;
+           count++;
+       }
+       buffer[count] = 0;
+
+       if (ch == '#')
+       {
+           xmitcsum = hex(pdb_get_char()) << 4;
+           xmitcsum += hex(pdb_get_char());
+
+           if (xmitcsum == checksum)
+           {
+               pdb_put_char('+');
+               if (buffer[2] == ':')
+               {
+                   printk ("gdb packet found with sequence ID\n");
+               }
+           }
+           else
+           {
+               pdb_put_char('-');
+           }
+       }
+    } while (checksum != xmitcsum);
+
+    return;
+}
+
+/*
+ * process a machine interrupt or exception
+ * return 1 if pdb is not interested in the exception; it should
+ * be propagated to the guest os.
+ */
+
+int pdb_handle_exception(int exceptionVector,
+                        struct pt_regs *xen_regs)
+{
+    int signal = 0;
+
+    printk ("pdb_handle_exception [0x%x][0x%lx]\n",
+           exceptionVector, xen_regs->eip);
+
+    /* if  pdb didn't set the breakpoint, and 
+           pdb is not single stepping, and
+          the user didn't press the magic debug key on the console,
+       then pass the exception up to the guest os */
+    if (pdb_bkpt_search(xen_regs->eip - 1) == NULL &&
+       pdb_stepping == 0 &&
+       exceptionVector != 0x88)
+    {
+        TRC(printk("pdb: external breakpoint at 0x%lx\n", xen_regs->eip));
+       return 1;
+    }
+
+    if (pdb_stepping == 1)
+    {
+        xen_regs->eflags &= ~0x100;
+        pdb_stepping = 0;
+    }
+
+    if (exceptionVector == 0x03)
+    {
+        xen_regs->eip --;
+    }
+
+    /* generate a signal for gdb */
+    switch (exceptionVector)
+    {
+    case 136 : signal = 2; break;                                  /* SIGINT */
+    case 1   : signal = 5; break;                                 /* SIGTRAP */
+    case 3   : signal = 5; break;                                 /* SIGTRAP */
+    default  :
+      printk ("can't generate signal for unknown exception vector %d\n",
+             exceptionVector);
+      break;
+    }
+
+    pdb_out_buffer[0] = 'S';
+    pdb_out_buffer[1] = hexchars[signal >> 4];
+    pdb_out_buffer[2] = hexchars[signal % 16];
+    pdb_out_buffer[3] = 0;
+    pdb_put_packet(pdb_out_buffer, 1);
+
+    while (1)
+    {
+        pdb_out_buffer[0] = 0;
+       pdb_get_packet(pdb_in_buffer);
+       if (pdb_process_command(pdb_in_buffer, xen_regs))
+       {
+           return 0;
+       }
+    }
+
+    return 0;
+}
+
+void pdb_key_pressed(u_char key, void *dev_id, struct pt_regs *regs) 
+{
+    pdb_handle_exception(136, regs);
+    return;
+}
+
+void initialize_pdb()
+{
+    extern char opt_pdb[];
+    int pdb_com_port;
+
+    if (strncmp(opt_pdb, "com", 3) == 0)
+    {
+        extern void debug_set_com_port(int port);
+
+        pdb_com_port = opt_pdb[3] - '0';                 /* error checking ? */
+       debug_set_com_port(pdb_com_port);
+       pdb_high_bit = opt_pdb[4] == 'H' ? 1 : 0;
+    }
+    else
+    {
+        if (strcmp(opt_pdb, "none") != 0)
+       {
+           printk ("pdb: unknown option\n");
+       }
+        return;
+    }
+
+    printk ("Initializing pervasive debugger (PDB) [%s] port %d, high %d\n",
+           opt_pdb, pdb_com_port, pdb_high_bit);
+
+    breakpoints.address = 0;
+    INIT_LIST_HEAD(&breakpoints.list);
+
+    pdb_stepping = 0;
+
+    /* ack any spurrious gdb packets */
+    pdb_put_char ('+');
+
+    /* serial console */
+    add_key_handler('D', pdb_key_pressed, "enter pervasive debugger");
+
+    pdb_initialized = 1;
+}
+
+void breakpoint(void)
+{
+    if (pdb_initialized)
+        asm("int $3");
+}
index b0f6766b9f926c92b0815f53779f33e012914f87..ee52629dee5525e4778a9099a2ef210095bdfe56 100644 (file)
@@ -12,6 +12,7 @@
 #include <asm/apic.h>
 #include <asm/desc.h>
 #include <asm/domain_page.h>
+#include <asm/pdb.h>
 
 struct cpuinfo_x86 boot_cpu_data = { 0 };
 /* Lots of nice things, since we only target PPro+. */
@@ -428,6 +429,7 @@ void __init start_of_day(void)
     do_initcalls();
     initialize_serial();   /* setup serial 'driver' (for debugging) */
     initialize_keyboard(); /* setup keyboard (also for debugging)   */
+    initialize_pdb();      /* pervasive debugger */
 
     if ( !setup_network_devices() )
         panic("Must have a network device!\n");
index 78c26c37ccee90977081a6dffd1113e0cb6abcb0..4f2cd6af669a254d2bdc99ae50b44e84cd12ab30 100644 (file)
@@ -49,6 +49,7 @@
 #include <asm/pgalloc.h>
 #include <asm/uaccess.h>
 #include <asm/i387.h>
+#include <asm/pdb.h>
 
 #define GTBF_TRAP        1
 #define GTBF_TRAP_NOCODE 2
@@ -223,6 +224,30 @@ static inline void do_trap(int trapnr, char *str,
           smp_processor_id(), trapnr, str, error_code);
 }
 
+static inline void do_int3_exception(int trapnr,
+                                    struct pt_regs *regs, 
+                                    long error_code)
+{
+    struct task_struct *p = current;
+    struct guest_trap_bounce *gtb = guest_trap_bounce+smp_processor_id();
+    trap_info_t *ti;
+
+    if ((regs->xcs & 3) != 3)
+    {
+        pdb_handle_exception(trapnr, regs);
+       return;
+    }
+
+    ti = current->thread.traps + trapnr;
+    gtb->flags      =  GTBF_TRAP_NOCODE;
+    gtb->error_code = error_code;
+    gtb->cs         = ti->cs;
+    gtb->eip        = ti->address;
+    if ( TI_GET_IF(ti) )
+        clear_bit(EVENTS_MASTER_ENABLE_BIT, &p->shared_info->events_mask);
+    return; 
+}
+
 #define DO_ERROR_NOCODE(trapnr, str, name) \
 asmlinkage void do_##name(struct pt_regs * regs, long error_code) \
 { \
@@ -236,7 +261,6 @@ do_trap(trapnr, str, regs, error_code, 1); \
 }
 
 DO_ERROR_NOCODE( 0, "divide error", divide_error)
-DO_ERROR_NOCODE( 3, "int3", int3)
 DO_ERROR_NOCODE( 4, "overflow", overflow)
 DO_ERROR_NOCODE( 5, "bounds", bounds)
 DO_ERROR_NOCODE( 6, "invalid operand", invalid_op)
@@ -251,6 +275,12 @@ DO_ERROR(17, "alignment check", alignment_check)
 DO_ERROR_NOCODE(18, "machine check", machine_check)
 DO_ERROR_NOCODE(19, "simd error", simd_coprocessor_error)
 
+asmlinkage void do_int3(struct pt_regs * regs, long error_code)
+{
+    if (pdb_initialized)   do_int3_exception(3, regs, error_code);
+    else                   do_trap(3, "int3", regs, error_code, 0);
+}
+
 asmlinkage void do_double_fault(void)
 {
     extern spinlock_t console_lock;
@@ -489,13 +519,44 @@ asmlinkage void math_state_restore(struct pt_regs *regs, long error_code)
     }
 }
 
-
 asmlinkage void do_debug(struct pt_regs * regs, long error_code)
 {
     unsigned int condition;
     struct task_struct *tsk = current;
     struct guest_trap_bounce *gtb = guest_trap_bounce+smp_processor_id();
 
+    /*
+    printk("do_debug_exceptionn [%lx][%lx][%x]\n",
+          error_code, regs->eip, regs->xcs);
+    */
+
+    __asm__ __volatile__("movl %%db6,%0" : "=r" (condition));
+
+    if ((condition & (1 << 14)) != (1 << 14))
+    {
+        printk ("\nwarning: debug trap w/o BS bit [0x%x]\n\n", condition);
+    }
+    __asm__("movl %0,%%db6" : : "r" (0));
+
+    if (pdb_handle_exception(1, regs))                /* propagate to domain */
+    {
+        tsk->thread.debugreg[6] = condition;
+
+       gtb->flags = GTBF_TRAP_NOCODE;
+       gtb->cs    = tsk->thread.traps[1].cs;
+       gtb->eip   = tsk->thread.traps[1].address;
+    }
+
+    return;
+}
+
+
+asmlinkage void do_debug_orig(struct pt_regs * regs, long error_code)
+{
+    unsigned int condition;
+    struct task_struct *tsk = current;
+    struct guest_trap_bounce *gtb = guest_trap_bounce+smp_processor_id();
+
     __asm__ __volatile__("movl %%db6,%0" : "=r" (condition));
 
     /* Mask out spurious debug traps due to lazy DR7 setting */
diff --git a/xen/common/debug-linux.c b/xen/common/debug-linux.c
new file mode 100644 (file)
index 0000000..8e4f0c6
--- /dev/null
@@ -0,0 +1,82 @@
+#include <xeno/config.h>
+#include <xeno/types.h>
+#include <xeno/lib.h>
+#include <hypervisor-ifs/dom0_ops.h>
+
+#include "debug-linux.h"
+
+/* 
+ * linux specific pdb stuff 
+ */
+
+/*
+  static inline struct task_struct *find_task_by_pid(int pid)
+  {
+    struct task_struct *p, **htable = &pidhash[pid_hashfn(pid)];
+
+    for(p = *htable; p && p->pid != pid; p = p->pidhash_next) ;
+    return p;
+  }
+*/
+
+/* read a byte from a process */
+u_char pdb_linux_get_value (int domain, int pid, unsigned long addr)
+{
+  u_char result = 0;
+  unsigned long task_struct_p, mm_p, pgd, task_struct_pid;
+  unsigned long l2tab, page;
+
+  /* find the task_struct of the given process */
+  pdb_get_values(domain, (u_char *) &task_struct_p, 
+                pidhash_addr + pid_hashfn(pid) * 4,
+                sizeof(task_struct_p));
+
+  /* find the correct task struct */
+  while (task_struct_p != (unsigned long)NULL)
+  {
+    pdb_get_values(domain, (u_char *) &task_struct_pid, 
+                  task_struct_p + task_struct_pid_offset,
+                  sizeof(task_struct_pid));
+    if (task_struct_pid == pid)
+    {
+      break;
+    }
+    
+    pdb_get_values(domain, (u_char *) &task_struct_p, 
+                  task_struct_p + task_struct_pidhash_next_offset,
+                  sizeof(task_struct_p));
+  }
+  if (task_struct_p == (unsigned long)NULL)
+  {
+    /* oops */
+    printk ("error: couldn't find process 0x%x in domain %d\n", pid, domain);
+    return 0;
+  }
+
+  /* get the mm_struct within the task_struct */
+  pdb_get_values(domain, (u_char *) &mm_p, 
+                task_struct_p + task_struct_mm_offset,
+                sizeof(mm_p));
+  /* get the page global directory (cr3) within the mm_struct */
+  pdb_get_values(domain, (u_char *) &pgd, 
+                mm_p + mm_struct_pgd_offset,
+                sizeof(pgd));
+
+  /* get the l2 table entry */
+  pdb_get_values(domain, (u_char *) &l2tab, 
+                pgd + (addr >> PGDIR_SHIFT) * 4,
+                sizeof(l2tab));
+  l2tab = (unsigned long)__va(machine_to_phys(domain, l2tab) & PAGE_MASK);
+
+  /* get the page table entry */
+  pdb_get_values(domain, (u_char *) &page,
+                l2tab + ((addr & L1_PAGE_BITS) >> PAGE_SHIFT) * 4,
+                sizeof(page));
+  page = (unsigned long)__va(machine_to_phys(domain, page) & PAGE_MASK);
+
+  /* get the byte */
+  pdb_get_values(domain, (u_char *) &result, page + (addr & ~PAGE_MASK),
+                sizeof(result));
+
+  return result;
+}
diff --git a/xen/common/debug-linux.h b/xen/common/debug-linux.h
new file mode 100644 (file)
index 0000000..ae93d34
--- /dev/null
@@ -0,0 +1,45 @@
+#include <asm/pdb.h>
+
+/* from linux/sched.h */
+#define PIDHASH_SZ (4096 >> 2)
+#define pid_hashfn(x)  ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
+
+/* from asm-xeno/pgtable-2level.h */
+#define PGDIR_SHIFT    22
+#define PTRS_PER_PGD   1024
+
+/* from asm-xeno/page.h */
+#define PAGE_SHIFT     12
+#define PAGE_SIZE      (1UL << PAGE_SHIFT)
+#define PAGE_MASK      (~(PAGE_SIZE-1))
+
+#define __PAGE_OFFSET          (0xC0000000)
+#define PAGE_OFFSET            ((unsigned long)__PAGE_OFFSET)
+#define __pa(x)                        ((unsigned long)(x)-PAGE_OFFSET)
+#define __va(x)                        ((void *)((unsigned long)(x)+PAGE_OFFSET))
+
+/* from debug.h */
+#define ENTRIES_PER_L1_PAGETABLE 1024
+#define L1_PAGE_BITS ( (ENTRIES_PER_L1_PAGETABLE - 1) << PAGE_SHIFT )
+
+
+/* adapted from asm-xeno/page.h */
+static inline unsigned long machine_to_phys(int domain, unsigned long machine)
+{
+  unsigned long phys;
+  pdb_get_values(domain, (u_char *) &phys,
+                (unsigned long) machine_to_phys_mapping + (machine >> PAGE_SHIFT) * 4,
+                sizeof(phys));
+  phys = (phys << PAGE_SHIFT) | (machine & ~PAGE_MASK);
+  return phys;
+}
+
+
+#define pidhash_addr 0xc018f260UL
+
+#define task_struct_mm_offset  0x2c
+#define task_struct_pid_offset 0x7c
+#define task_struct_pidhash_next_offset 0xb0
+#define mm_struct_pgd_offset   0x0c
+
+extern u_char pdb_linux_get_value (int domain, int pid, unsigned long addr);
index f233c4e1f7704d602ee88182300e4dabba001f8d..852b3bca0335d4e1bb0c162cce7d2900f95a4937 100644 (file)
 #include <hypervisor-ifs/dom0_ops.h>
 #include <xeno/sched.h>
 #include <xeno/event.h>
+#include <asm/page.h>
+#include <asm/domain_page.h>                           /* [un]map_domain_mem */
+#include <asm/pdb.h>
 
-#define DEBUG_TRACE
+#undef DEBUG_TRACE
 #ifdef DEBUG_TRACE
 #define TRC(_x) _x
 #else
 #define TRC(_x)
 #endif
 
+/****************************************************************************/
+
+int pdb_change_values (int domain, u_char *buffer, unsigned long addr,
+                      int length, int rw);
+
+/*
+ * Set memory in a domain's address space
+ * Set "length" bytes at "address" from "domain" to the values in "buffer".
+ * Return the number of bytes set, 0 if there was a problem.
+ *
+ * THIS WILL BECOME A MACRO
+ */
+
+int pdb_set_values (int domain, u_char *buffer, unsigned long addr, int length)
+{
+    int count;
+    void *bkpt;
+    count = pdb_change_values(domain, buffer, addr, length, 2);
+
+    /* this is a bit x86 specific at the moment... */
+    if (length == 1 && buffer[0] == 'c' && buffer[1] == 'c')
+    {
+        /* inserting a new breakpoint */
+        pdb_bkpt_add (addr);
+        TRC(printk("pdb breakpoint detected at 0x%lx\n", addr));
+    }
+    else if ((bkpt = pdb_bkpt_search(addr)))
+    {
+        /* removing a breakpoint */
+        TRC(printk("pdb breakpoint cleared at 0x%lx\n", addr));
+        pdb_bkpt_remove_ptr(bkpt);
+    }
+
+    return count;
+}
+
+/*
+ * Read memory from a domain's address space.
+ * Fetch "length" bytes at "address" from "domain" into "buffer".
+ * Return the number of bytes read, 0 if there was a problem.
+ *
+ * THIS WILL BECOME A MACRO
+ */
+
+int pdb_get_values (int domain, u_char *buffer, unsigned long addr, int length)
+{
+    return pdb_change_values(domain, buffer, addr, length, 1);
+}
+
+/*
+ * Change memory in  a domain's address space.
+ * Read or write "length" bytes at "address" from "domain" into/from "buffer".
+ * Return the number of bytes read, 0 if there was a problem.
+ * RW: 1 = read, 2 = write
+ */
+
+int pdb_change_values (int domain, u_char *buffer, unsigned long addr,
+                      int length, int rw)
+{
+    struct task_struct *p;
+    l2_pgentry_t* l2_table = NULL;
+    l1_pgentry_t* l1_table = NULL;
+    u_char *page;
+    int bytes = 0;
+
+    extern char *hex2mem (char *, char *, int);
+
+    p = find_domain_by_id(domain);
+
+    if ((addr >> PAGE_SHIFT) == ((addr + length - 1) >> PAGE_SHIFT))
+    {
+        l2_table = map_domain_mem(pagetable_val(p->mm.pagetable));
+       l2_table += l2_table_offset(addr);
+       if (!(l2_pgentry_val(*l2_table) & _PAGE_PRESENT)) 
+       {
+           printk ("L2:0x%p (0x%lx) \n", l2_table, l2_pgentry_val(*l2_table));
+           goto exit2;
+       }
+
+       if (l2_pgentry_val(*l2_table) & _PAGE_PSE)
+       {
+#define PSE_PAGE_SHIFT           L2_PAGETABLE_SHIFT
+#define PSE_PAGE_SIZE           (1UL << PSE_PAGE_SHIFT)
+#define PSE_PAGE_MASK           (~(PSE_PAGE_SIZE-1))
+
+#define L1_PAGE_BITS ( (ENTRIES_PER_L1_PAGETABLE - 1) << L1_PAGETABLE_SHIFT )
+
+#define pse_pgentry_to_phys(_x) (l2_pgentry_val(_x) & PSE_PAGE_MASK)
+
+           page = map_domain_mem(pse_pgentry_to_phys(*l2_table) +/* 10 bits */
+                                 (addr & L1_PAGE_BITS));         /* 10 bits */
+           page += addr & (PAGE_SIZE - 1);                       /* 12 bits */
+       }
+       else
+       {
+           l1_table = map_domain_mem(l2_pgentry_to_phys(*l2_table));
+           l1_table += l1_table_offset(addr); 
+           if (!(l1_pgentry_val(*l1_table) & _PAGE_PRESENT))
+           {
+               printk ("L2:0x%p (0x%lx) L1:0x%p (0x%lx)\n", 
+                       l2_table, l2_pgentry_val(*l2_table),
+                       l1_table, l1_pgentry_val(*l1_table));
+               goto exit1;
+           }
+
+           page = map_domain_mem(l1_pgentry_to_phys(*l1_table));
+           page += addr & (PAGE_SIZE - 1);
+       }
+
+       switch (rw)
+       {
+       case 1:                                                      /* read */
+           memcpy (buffer, page, length);
+           bytes = length;
+           break;
+       case 2:                                                     /* write */
+           hex2mem (buffer, page, length);
+           bytes = length;
+           break;
+       default:                                                  /* unknown */
+           printk ("error: unknown RW flag: %d\n", rw);
+           return 0;
+       }
+
+       unmap_domain_mem((void *)page); 
+    exit1:
+       if (l1_table != NULL)
+           unmap_domain_mem((void *)l1_table);
+    exit2:
+       unmap_domain_mem((void *)l2_table);
+    }
+    else
+    {
+        /* read spans pages. need to recurse */
+        printk ("pdb memory SPAN! addr:0x%lx l: %x\n", addr, length);
+    }
+
+    put_task_struct(p);
+    return bytes;
+}
+
+
+/*
+ * interactively call pervasive debugger from a privileged domain
+ */
 void pdb_do_debug (dom0_op_t *op)
 {
     op->u.debug.status = 0;
-    op->u.debug.out1 = op->u.debug.in2 + 10;
-    op->u.debug.out2 = op->u.debug.in1 + 100;
 
-    TRC(printk("PDB: op:%c, dom:%x, in1:%x, in2:%x\n",
+    TRC(printk("PDB: op:%c, dom:%x, in1:%x, in2:%x, in3:%x, in4:%x\n",
               op->u.debug.opcode, op->u.debug.domain,
-              op->u.debug.in1, op->u.debug.in2));
+              op->u.debug.in1, op->u.debug.in2,
+              op->u.debug.in3, op->u.debug.in4));
 
+    /* NOT NOW
     if (op->u.debug.domain == 0)
     {
         op->u.debug.status = 1;
        return;
     }
+    */
 
     switch (op->u.debug.opcode)
     {
-        case 'r' :
+        case 'c' :
        {
            struct task_struct * p = find_domain_by_id(op->u.debug.domain);
            if ( p != NULL )
@@ -54,6 +203,28 @@ void pdb_do_debug (dom0_op_t *op)
            }
            break;
        }
+        case 'r' :
+        {
+            int loop;
+            u_char x;
+
+            for (loop = 0; loop < op->u.debug.in2; loop++)         /* length */
+            { 
+               extern u_char pdb_linux_get_value (int domain, int pid, unsigned long addr);
+
+                if (loop % 8 == 0)
+                {
+                    printk ("\n%08x ", op->u.debug.in1 + loop);
+                }
+                x = pdb_linux_get_value(op->u.debug.domain,        /* domain */
+                                       op->u.debug.in3,             /* pid */
+                                       op->u.debug.in1 + loop);     /* addr */
+                printk (" %02x", x);
+            }
+            printk ("\n");
+            break;
+        }
+
         case 's' :
        {
            unsigned long cpu_mask;
index 1bba43d7bec2477e281b08f911c3fa23ac427f1e..86138c59e658fc42d2aba6a6360c008df568a7f2 100644 (file)
@@ -69,6 +69,8 @@ int opt_noreboot=0;
 int opt_ignorebiostables=0;
 /* opt_watchdog: If true, run a watchdog NMI on each processor. */
 int opt_watchdog=0;
+/* opt_pdb: Name of serial port for Xen pervasive debugger (and enable pdb) */
+unsigned char opt_pdb[10] = "none";
 
 static struct {
     unsigned char *name;
@@ -85,6 +87,7 @@ static struct {
     { "noreboot",         OPT_BOOL, &opt_noreboot },
     { "ignorebiostables", OPT_BOOL, &opt_ignorebiostables },
     { "watchdog",         OPT_BOOL, &opt_watchdog },
+    { "pdb",              OPT_STR,  &opt_pdb },
     { NULL,               0,        NULL     }
 };
 
index c6457bf998ecb80648590a0beadf95e9c81acb00..582cccbc75fd1caa04508d850ebb53d7a24f426d 100644 (file)
 #define NS16550_MCR_OUT2        0x08    /* OUT2: interrupt mask */
 #define NS16550_MCR_LOOP       0x10    /* Loop                 */
 
-#define SERIAL_BASE 0x3f8  /* XXX SMH: horrible hardwired COM1   */
+#define LSR_DR   0x01  /* Data ready */
+#define LSR_OE   0x02  /* Overrun */
+#define LSR_PE   0x04  /* Parity error */
+#define LSR_FE   0x08  /* Framing error */
+#define LSR_BI   0x10  /* Break */
+#define LSR_THRE 0x20  /* Xmit holding register empty */
+#define LSR_TEMT 0x40  /* Xmitter empty */
+#define LSR_ERR  0x80  /* Error */
 
-static int serial_echo = 0;   /* default is not to echo; change with 'e' */
+#define SERIAL_COM1 0x3f8
+#define SERIAL_COM2 0x2f8
 
+int serial_com_base = SERIAL_COM1;
+int debug_com_base  = SERIAL_COM1;
+
+
+static int serial_echo = 0;       /* default is not to echo; change with '~' */
 
 void toggle_echo(u_char key, void *dev_id, struct pt_regs *regs) 
 {
     serial_echo = !serial_echo; 
 }
 
+void debug_set_com_port(int port)
+{
+    debug_com_base = port == 1 ? SERIAL_COM1 : SERIAL_COM2;
+}
+
+int debug_testchar()                                /* character available? */
+{
+    return (inb(debug_com_base + NS16550_LSR) & LSR_DR);
+}
+
+u_char debug_getchar()
+{
+    while (! (inb(debug_com_base + NS16550_LSR) & LSR_DR));/* wait for char */
+    return inb(debug_com_base + NS16550_RBR);
+}
+
+void debug_putch(u_char c)
+{
+    while (! (inb(debug_com_base + NS16550_LSR) & LSR_THRE));
+                                                            /* wait for idle */
+    outb(c, debug_com_base + NS16550_RBR);
+}
+
+void debug_putchar(u_char c)
+{
+    debug_putch(c);
+    if (c == '\n') debug_putch('\r');
+}
+
+
+
+int serial_testchar()                                /* character available? */
+{
+    return (inb(serial_com_base + NS16550_LSR) & LSR_DR);
+}
+
+u_char serial_getchar()
+{
+    while (! (inb(serial_com_base + NS16550_LSR) & LSR_DR));/* wait for char */
+    return inb(serial_com_base + NS16550_RBR);
+}
+
+void serial_putch(u_char c)
+{
+    while (! (inb(serial_com_base + NS16550_LSR) & LSR_THRE));
+                                                            /* wait for idle */
+    outb(c, serial_com_base + NS16550_RBR);
+}
+
+void serial_putchar(u_char c)
+{
+    serial_putch(c);
+    if (c == '\n') serial_putch('\r');
+}
+
+static spinlock_t serial_lock;
+
 static void serial_rx_int(int irq, void *dev_id, struct pt_regs *regs)
 {
     u_char c; 
     key_handler *handler; 
+    unsigned long flags;
 
-    while ( (inb(SERIAL_BASE + NS16550_LSR) & 1) == 1 )
-    {
-        c = inb(SERIAL_BASE + NS16550_RBR);
-
-        if( (handler = get_key_handler(c)) != NULL ) 
-            (*handler)(c, dev_id, regs); 
+    spin_lock_irqsave(&serial_lock, flags);
 
-        if ( serial_echo ) 
-            printk("%c", c);
-    } 
+    while (serial_testchar())
+    {
+        c = serial_getchar();
+
+       if (c & 0x80)
+       {
+           extern int pdb_serial_input(u_char, struct pt_regs *);
+           pdb_serial_input(c & 0x7f, regs);
+       }
+       else
+       {
+           if ( (handler = get_key_handler(c)) != NULL ) 
+               (*handler)(c, dev_id, regs); 
+
+           if ( serial_echo ) 
+               serial_putch(c);
+       }
+    }
+
+    spin_unlock_irqrestore(&serial_lock, flags);
 }
 
 void initialize_serial() 
@@ -74,6 +157,8 @@ void initialize_serial()
 
     if ( !SERIAL_ENABLED )
         return;
+
+    spin_lock_init(&serial_lock);
     
     /* setup key handler */
     add_key_handler('~', toggle_echo, "toggle serial echo");
@@ -82,13 +167,13 @@ void initialize_serial()
     /* Clear FIFOs, enable, trigger at 1 byte */
     outb(NS16550_FCR_TRG1 | NS16550_FCR_ENABLE |
          NS16550_FCR_CLRX  | NS16550_FCR_CLTX, 
-         SERIAL_BASE+NS16550_FCR);
+         serial_com_base + NS16550_FCR);
 
     /* Enable receive interrupts. Also remember to keep DTR/RTS asserted. */
     outb(NS16550_MCR_OUT2|NS16550_MCR_DTR|NS16550_MCR_RTS, 
-         SERIAL_BASE + NS16550_MCR);
+         serial_com_base + NS16550_MCR);
     outb(NS16550_IER_ERDAI, 
-         SERIAL_BASE + NS16550_IER );
+         serial_com_base + NS16550_IER );
 
     if( (rc = request_irq(4, serial_rx_int, SA_NOPROFILE, "serial", 0)) )
        printk("initialize_serial: failed to get IRQ4, rc=%d\n", rc); 
diff --git a/xen/include/asm-i386/pdb.h b/xen/include/asm-i386/pdb.h
new file mode 100644 (file)
index 0000000..5799328
--- /dev/null
@@ -0,0 +1,41 @@
+
+/*
+ * pervasive debugger
+ *
+ * alex ho
+ * 2004
+ * university of cambridge computer laboratory
+ */
+
+
+#ifndef __PDB_H__
+#define __PDB_H__
+
+#include <asm/ptrace.h>
+#include <xeno/list.h>
+
+extern int pdb_initialized;
+extern int pdb_com_port;
+extern int pdb_high_bit;
+
+extern void initialize_pdb(void);
+extern int pdb_set_values (int domain, u_char *buffer, 
+                          unsigned long addr, int length);
+extern int pdb_get_values (int domain, u_char *buffer,
+                          unsigned long addr, int length);
+
+extern int pdb_handle_exception(int exceptionVector,
+                               struct pt_regs *xen_regs);
+
+
+struct pdb_breakpoint
+{
+    struct list_head list;
+    unsigned long address;
+};
+extern void pdb_bkpt_add (unsigned long address);
+extern struct pdb_breakpoint* pdb_bkpt_search (unsigned long address);
+extern void pdb_bkpt_remove_ptr (struct pdb_breakpoint *bkpt);
+extern int pdb_bkpt_remove (unsigned long address);
+
+#endif  /* __PDB_H__ */
index e1d20ff2c7fe24c3cc72c8bed0cd605f40d3ed53..e260c1c033625ad8729114c9605b9f3942a76ae7 100644 (file)
@@ -17,7 +17,7 @@
  * This makes sure that old versions of dom0 tools will stop working in a
  * well-defined way (rather than crashing the machine, for instance).
  */
-#define DOM0_INTERFACE_VERSION   0xAAAA0003
+#define DOM0_INTERFACE_VERSION   0xAAAA0004
 
 
 /*
@@ -168,9 +168,11 @@ typedef struct dom0_debug_st
 {
     /* IN variables. */
     char opcode;
-    int domain, in1, in2;
+    unsigned int domain;
+    int in1, in2, in3, in4;
     /* OUT variables. */
-    int status, out1, out2;
+    unsigned int status;
+    int out1, out2;
 } dom0_debug_t;
 
 /*
index 2e9cd0c563b27d8aae9173f5f12fa9c6d9a13b5b..9ac28fa8ede80393d0a947a816f4a7243434ba89 100644 (file)
@@ -159,7 +159,7 @@ struct task_struct
  *                       or expiring timer
  * TASK_UNINTERRUPTIBLE: Domain is blocked but may not be woken up by an
  *                       arbitrary event or timer.
- * TASK_STOPPED:         Domain is sopped.
+ * TASK_STOPPED:         Domain is stopped.
  * TASK_DYING:           Domain is about to cross over to the land of the dead.
  */